package ChatSystem;

import java.util.concurrent.LinkedBlockingDeque;

/**
 * A generic Sender which sends instances of a specified ChatMessage subclass over an instance of a specified class.
 */
public abstract class Sender<MessageType extends ChatMessage, SenderType> extends Thread {
	private volatile boolean _running;
	private final LinkedBlockingDeque<MessageType> _msgToSend;
	private final Object _lock = new Object();
	protected final SenderType _sender;
	
	/**
	 * Initializes the sender.
	 * @param sender The object to send the messages with, provided by subclasses
	 */
	public Sender(SenderType sender) {
		_running = true;
		_sender = sender;
		_msgToSend = new LinkedBlockingDeque<MessageType>();
		
		this.start();
	}
	
	/**
	 * Adds a message to the messages queue. It will not be sent until every messages already in the queue are processed.
	 * @param message The message to enqueue
	 */
	public void postMessage(MessageType message) {
		_msgToSend.add(message);
		synchronized(_lock) {
			_lock.notify();
		}
	}
		
	/**
	 * The object's sending loop. Will send messages as available until stopSender() is called.
	 */
	public void run() {
		while(_running) {
			MessageType msg = _msgToSend.poll();
			if(msg != null) {
				this.sendMessage(msg);
			}
			else {
				try {
					synchronized(_lock) {
						_lock.wait();
					}
				}
				catch (InterruptedException e) {}
			}
		}
	}
	
	/**
	 * Stops the execution of the sender by interrupting the sending loop. Any message not sent will be discarded.
	 */
	public void stopSender() {
		_running = false;
		synchronized(_lock) {
			_lock.notify();
		}
		this.cancel();
		
		try {
			this.join();
		} catch (InterruptedException e) {}
	}
	
	/**
	 * Cancels the Sender. Its role is to ask the underlying sending object to interrupt itself, if necessary.
	 */
	protected abstract void cancel();
	
	/**
	 * Sends a message over the internal sender object.
	 * @param message The message to send
	 */
	protected abstract void sendMessage(MessageType message);
}
